package search

import (
	b "code.google.com/p/gochess/board"
	pos "code.google.com/p/gochess/position"
)

// using this interface free version increases nps by 5%
func makeMove(p *position, move pos.Move) pos.MoveUnmaking {
	start, dst := move.Start(), move.Destination()
	halfmoveClock := p.HalfmoveClock() + 1
	captured := p.Get(dst)
	var captureIdx int8
	cr := p.CastlingRights()
	// deal with captures
	if captured != pos.NoPiece {
		captureIdx = p.Remove(dst)
		halfmoveClock = 0
		if cr != pos.NoCastlingRights && captured.Type() == pos.Rook {
			opponent := pos.Opponent(p.SideToMove())
			switch dst {
			case pos.Castlings[opponent][pos.CastlingShort].RookLocation:
				// color spoiled short castling for opponent
				p.SetCastlingRights(cr.Remove(opponent, pos.CastlingShort))
			case pos.Castlings[opponent][pos.CastlingLong].RookLocation:
				// color spoiled long castling for opponent
				p.SetCastlingRights(cr.Remove(opponent, pos.CastlingLong))
			}
		}
	}
	var epTarget b.Square
	var promoIdx int8
	moved := p.Get(start)
	if moved.Type() == pos.Pawn {
		// pawn moves
		halfmoveClock = 0
		switch {
		case move.CapturesEnPassant():
			captureIdx = p.Remove(dst - pos.MoveDirection(p.SideToMove()))
		case move.Promotion() != pos.NoPieceType:
			promoIdx = p.Remove(start)
			p.Set(pos.NewPiece(p.SideToMove(), move.Promotion()), start)
		case dst-start == pos.MoveDirection(p.SideToMove())<<1:
			epTarget = start + pos.MoveDirection(p.SideToMove())
		}
	} else {
		// no pawn move
		if move.Castling() != pos.NoCastling {
			castling := &pos.Castlings[p.SideToMove()][move.Castling()]
			// move the king first, as the rook move is saved in the move
			p.Move(castling.KingLocation, castling.KingDestination)
			p.SetCastlingRights(p.CastlingRights().Remove(p.SideToMove(), pos.CastlingBoth))
		} else {
			// check if stm looses castling rights
			if p.CastlingRights() != pos.NoCastlingRights {
				switch {
				case moved.Type() == pos.King:
					p.SetCastlingRights(p.CastlingRights().Remove(p.SideToMove(), pos.CastlingBoth))
				case moved.Type() == pos.Rook:
					switch start {
					case pos.Castlings[p.SideToMove()][pos.CastlingLong].RookLocation:
						p.SetCastlingRights(p.CastlingRights().Remove(p.SideToMove(), pos.CastlingLong))
					case pos.Castlings[p.SideToMove()][pos.CastlingShort].RookLocation:
						p.SetCastlingRights(p.CastlingRights().Remove(p.SideToMove(), pos.CastlingShort))
					}
				}
			}
		}
	}
	unmake := pos.NewMoveUnmaking(move, captured, p.EnPassantTarget(), p.HalfmoveClock(),
		cr, captureIdx, promoIdx)
	p.SetHalfmoveClock(halfmoveClock)
	p.SetEnPassantTarget(epTarget)
	p.SwitchSides()
	p.Move(start, dst)
	return unmake
}

func unmakeMove(p *position, uMove pos.MoveUnmaking) {
	p.SwitchSides()
	move := uMove.Move()
	if move.IsSpecial() {
		switch {
		case move.Promotion() != pos.NoPieceType:
			p.Remove(move.Destination())
			p.Reset(pos.NewPiece(p.SideToMove(), pos.Pawn), move.Destination(), uMove.PromotionIndex())
		case move.Castling() != pos.NoCastling:
			p.Move(pos.Castlings[p.SideToMove()][move.Castling()].KingDestination,
				pos.Castlings[p.SideToMove()][move.Castling()].KingLocation)
		case move.CapturesEnPassant():
			p.Reset(pos.NewPiece(pos.Opponent(p.SideToMove()), pos.Pawn),
				uMove.EnPassantTarget()-pos.MoveDirection(p.SideToMove()), uMove.CaptureIndex())
		}
	}
	p.Move(move.Destination(), move.Start())
	if uMove.CapturedPiece() != pos.NoPiece {
		p.Reset(uMove.CapturedPiece(), move.Destination(), uMove.CaptureIndex())
	}
	p.SetCastlingRights(uMove.CastlingRights())
	p.SetHalfmoveClock(uMove.HalfmoveClock())
	p.SetEnPassantTarget(uMove.EnPassantTarget())
}

func makeNullMove(p *position) (epTarget b.Square, halfmoveClock int32) {

	epTarget = p.EnPassantTarget()
	halfmoveClock = p.HalfmoveClock()
	p.SetEnPassantTarget(b.NoSquare)
	p.SetHalfmoveClock(0)
	p.SwitchSides()
	return
}

func unmakeNullMove(p *position, epTarget b.Square, halfmoveClock int32) {

	p.SetHalfmoveClock(halfmoveClock)
	p.SetEnPassantTarget(epTarget)
	p.SwitchSides()
}
